home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 June
/
Macworld (1999-06).dmg
/
Shareware World
/
Info
/
For Developers
/
MacZoop2.0.sea
/
MacZoop2.0
/
Required Classes
/
ZWindow.cpp
< prev
next >
Wrap
Text File
|
1999-02-25
|
64KB
|
2,506 lines
/*************************************************************************************************
*
*
* MacZoop - "the framework for the rest of us"
*
*
*
* ZWindow.cpp -- the window object
*
*
*
*
*
* © 1996, Graham Cox
*
* 14/11/96- modified to support undo and printing.
*
*
*************************************************************************************************/
#include "ZWindow.h"
#include "MacZoop.h"
#include "ZEventHandler.h"
#include "ZUndoTask.h"
#include "ProjectSettings.h"
#if _INSTALL_STD_MOUSE_TRACKING
#include "ZMouseTracker.h"
#endif
#if APPEARANCE_MGR_AWARE
#include "Appearance.h"
#endif
#if _WPOS_WINDOW_PLACEMENT
#include "ZResourceFile.h"
#endif
static short CalculateOffsetAmount( short idealStartPoint,
short idealEndPoint,
short idealOnScreenStartPoint,
short idealOnScreenEndPoint,
short screenEdge1,
short screenEdge2 );
static pascal OSErr ZWTrackingHandler( DragTrackingMessage theMsg, WindowPtr theWindow, void* refCon,
DragReference theDrag );
static pascal OSErr ZWDropHandler( WindowPtr theWindow, void* refCon, DragReference theDrag );
static DragTrackingHandlerUPP gDragTrackProc = NewDragTrackingHandlerProc( ZWTrackingHandler );
static DragReceiveHandlerUPP gDragReceiveProc = NewDragReceiveHandlerProc( ZWDropHandler );
extern OSErr gDragErr;
CLASSCONSTRUCTOR( ZWindow );
/*------------------------------*** CONSTRUCTOR ***---------------------------------*/
ZWindow::ZWindow( ZCommander* aBoss, const short windowID )
: ZCommander( aBoss )
{
classID = CLASS_ZWindow;
windID = windowID;
macWindow = NULL;
dirty = FALSE;
isNamed = FALSE;
stationeryFile = FALSE;
macFile.vRefNum = kNoFile;
macFType = 0;
printable = FALSE;
isPrinting = FALSE;
floating = FALSE;
disableAutoClose = FALSE;
SetRect( &zoomSource, 0, 0, 0, 0 );
// initial sizeRect is set to an arbitrary size
SetRect( &sizeRect,120 ,90 ,2000, 2000 );
winBackColour.red = winBackColour.green = winBackColour.blue = 0xFFFF;
}
ZWindow::ZWindow()
: ZCommander()
{
classID = CLASS_ZWindow;
windID = 0;
macWindow = NULL;
dirty = FALSE;
isNamed = FALSE;
stationeryFile = FALSE;
macFile.vRefNum = kNoFile;
macFType = 0;
printable = FALSE;
isPrinting = FALSE;
floating = FALSE;
disableAutoClose = FALSE;
SetRect( &zoomSource, 0, 0, 0, 0 );
SetRect( &sizeRect, 0, 0, 0, 0 );
winBackColour.red = winBackColour.green = winBackColour.blue = 0xFFFF;
}
/*------------------------------*** DESTRUCTOR ***---------------------------------*/
ZWindow::~ZWindow()
{
// set the current port to something else
if ( FrontWindow())
SetPort( FrontWindow());
// if the application has an undo task pertaining to this window, delete it.
ZUndoTask* curTask = gApplication->GetUndoTask();
if ( curTask && ( curTask->GetUndoTarget() == this ))
gApplication->SetTask( NULL );
// wMgr no longer needs us
gWindowManager->RemoveWindow( this );
if ( macWindow )
{
#if _AUTO_WPOS_FOR_FLOATERS
if ( floating )
SavePosition();
#endif
if ( MacHasDM())
RemoveDragHandlers();
DisposeWindow( macWindow );
}
macWindow = NULL;
}
/*--------------------------------*** INITZWINDOW ***---------------------------------*/
/*
initialise this object. By default, this just makes the window
----------------------------------------------------------------------------------------*/
void ZWindow::InitZWindow()
{
MakeMacWindow( windID );
if ( MacHasDM())
InstallDragHandlers();
// tell the window manager of our existence. This must be done after the
// full build of the mac window since the window manager needs to get
// information from it. Thus if you override this method, make sure you
// make the same call.
gWindowManager->AddWindow( this );
#if _AUTO_WPOS_FOR_FLOATERS
if ( floating )
RestorePosition();
#endif
}
/*------------------------------*** MAKEMACWINDOW ***---------------------------------*/
/*
create the macintosh window that this object looks after
----------------------------------------------------------------------------------------*/
void ZWindow::MakeMacWindow( const short windID )
{
if ( gMacInfo.supportsColour )
FailNIL( macWindow = GetNewCWindow( windID, NULL, NULL ));
else
FailNIL( macWindow = GetNewWindow( windID, NULL, NULL ));
// store the background colour so we can avoid GetAuxWin, etc.
SetPort( macWindow );
GetBackColor( &winBackColour );
// so we can identify the window, we set the windowKind to a special value. The refCon
// contains the object reference so we can freely locate the object from the window and
// vice versa. Your application must not touch the refCon- add members to your window
// objects instead.
((WindowPeek) macWindow)->windowKind = IS_ZWINDOW_KIND;
SetWRefCon( macWindow, (long) this );
// what WDEF are we using for this window? If it's one of the known "floater" types,
// then we should set the floating flag automatically- one less thing the programmer
// needs to worry about, and will work for the common cases.
WindTemplateHdl wTH = (WindTemplateHdl) GetResource( 'WIND', windID );
if ( wTH )
{
// the system floater has a proc of 124. The commonly used "Infinity" windoid
// has a proc of 128.
short pID = (*wTH)->procID / 16;
// with appearance manager, the WDEF IDs for floating windows are 66 & 67
#if APPEARANCE_MGR_AWARE
if ( gMacInfo.hasAppearanceMgr )
floating |= ( pID == kWindowUtilityDefProcResID ||
pID == kWindowUtilitySideTitleDefProcResID );
#endif
floating |= ( pID == kFloatingWindowDefinition ||
pID == kInfinityWindoidDefinition );
ReleaseResource((Handle) wTH );
}
// allocate a unique title. This we'll base on the current title
// (which should be "untitled"), with an appended digit to ensure uniqueness
Str255 wTitle;
GetWTitle( macWindow, wTitle );
if ( gWindowManager->GetUniqueUntitledName( wTitle ))
SetWTitle( macWindow, wTitle );
CopyPString( wTitle, macFile.name );
// if appearance savvy, make a root control for the window so stuff can be
// embedded into it easily.
#if APPEARANCE_MGR_AWARE
if ( gMacInfo.hasAppearanceMgr )
{
ControlHandle rc;
FailOSErr( CreateRootControl( macWindow, &rc ));
}
#endif
}
/*------------------------------*** MAKEMACWINDOW ***---------------------------------*/
void ZWindow::MakeMacWindow( Rect* aRect, Str255 title, Boolean visible, short varCode, Boolean hasCloseBox, void* userData )
{
if ( gMacInfo.supportsColour )
FailNIL( macWindow = NewCWindow( NULL, aRect, title, visible, varCode, NULL, hasCloseBox, 0 ));
else
FailNIL( macWindow = NewWindow( NULL, aRect, title, visible, varCode, NULL, hasCloseBox, 0 ));
((WindowPeek) macWindow)->windowKind = IS_ZWINDOW_KIND;
SetWRefCon( macWindow, (long) this );
CopyPString( title, macFile.name );
// if appearance savvy, make a root control for the window so stuff can be
// embedded into it easily.
#if APPEARANCE_MGR_AWARE
if ( gMacInfo.hasAppearanceMgr )
{
ControlHandle rc;
FailOSErr( CreateRootControl( macWindow, &rc ));
}
#endif
}
#pragma mark -
/*----------------------------------*** ACTIVATE ***----------------------------------*/
/*
this window is becoming active
----------------------------------------------------------------------------------------*/
void ZWindow::Activate()
{
DrawGrow();
}
/*---------------------------------*** DEACTIVATE ***---------------------------------*/
/*
this window is becoming inactive
----------------------------------------------------------------------------------------*/
void ZWindow::Deactivate()
{
DrawGrow();
}
/*------------------------------------*** FOCUS ***-----------------------------------*/
/*
make this window the current port ready for drawing or clicking
----------------------------------------------------------------------------------------*/
void ZWindow::Focus()
{
SetGWorld((CGrafPtr) macWindow, GetMainDevice());
SetOrigin( 0, 0 );
ClipRect( &macWindow->portRect );
}
/*--------------------------------*** POSTREFRESH ***---------------------------------*/
/*
invalidate the window contents so that it gets redrawn on the next update
----------------------------------------------------------------------------------------*/
void ZWindow::PostRefresh()
{
Rect r;
Focus();
GetContentRect( &r );
InvalRect( &r );
}
/*--------------------------------*** POSTREFRESH ***---------------------------------*/
/*
invalidate the rect passed
----------------------------------------------------------------------------------------*/
void ZWindow::PostRefresh( Rect* aRect )
{
Focus();
InvalRect( aRect );
}
/*------------------------------*** PERFORMUPDATE ***---------------------------------*/
/*
handle an update event. This is normally called from the event handler object, but you
can call it directly for special purposes if you know there is an update that needs
handling.
----------------------------------------------------------------------------------------*/
void ZWindow::PerformUpdate()
{
GrafPtr savePort;
GetPort( &savePort );
Focus();
// start the update sequence
BeginUpdate( macWindow );
if ( ! EmptyRgn( macWindow->visRgn ))
{
try
{
Draw();
}
catch( OSErr err )
{
EndUpdate( macWindow );
SetPort( savePort );
throw err;
}
}
EndUpdate( macWindow );
SetPort( savePort );
}
/*------------------------------------*** DRAW ***------------------------------------*/
/*
draw the contents of this window. This is the main draw dispatcher.
----------------------------------------------------------------------------------------*/
void ZWindow::Draw()
{
// if we are growable, we should clip out the growbox area otherwise
// the DrawContent call may erase the growbox, etc
short v = GetWVariant(macWindow);
if (v == documentProc || v == zoomDocProc)
{
Rect r = macWindow->portRect;
ClipRect( &r );
r.left = r.right - kStdScrollbarWidth;
r.top = r.bottom - kStdScrollbarWidth;
RectRgn( gUtilRgn, &r );
DiffRgn( macWindow->clipRgn, gUtilRgn, macWindow->clipRgn );
}
DrawContent();
SetOrigin( 0, 0 );
ClipRect( &macWindow->portRect );
DrawControls( macWindow );
DrawGrow();
}
/*-------------------------------*** DRAWCONTENT ***----------------------------------*/
/*
draw the content area of this window- this is what will draw the meaningful part
----------------------------------------------------------------------------------------*/
void ZWindow::DrawContent()
{
if (! isPrinting)
EraseRect( &macWindow->portRect );
}
/*----------------------------*** SETDEFAULTCOLOURS ***-------------------------------*/
/*
restores the window's default colours after drawing code may have changed it.
----------------------------------------------------------------------------------------*/
void ZWindow::SetDefaultColours()
{
Focus();
ForeColor( blackColor );
if ( IsColourPort( macWindow ))
RGBBackColor( &winBackColour );
else
BackColor( whiteColor );
}
/*---------------------------------*** DRAWGROW ***-----------------------------------*/
/*
draw the grow icon if the window type indicates this is appropriate
----------------------------------------------------------------------------------------*/
void ZWindow::DrawGrow()
{
if ( macWindow )
{
short v = GetWVariant( macWindow );
if ( v == documentProc || v == zoomDocProc )
{
Rect r = macWindow->portRect;
r.left = r.right - kStdScrollbarWidth;
r.top = r.bottom - kStdScrollbarWidth;
ClipRect( &r );
DrawGrowIcon( macWindow );
}
}
}
#pragma mark -
/*---------------------------------*** CALCPAGES ***----------------------------------*/
/*
compute the pagination for printing. By default this divides the contect rect up to be
tiled across the paper rect, horizontal major order. Override for other methods
----------------------------------------------------------------------------------------*/
void ZWindow::CalcPages( const Rect& paperRect, short* pagesH, short* pagesV )
{
Rect cr;
short pWidth, pHeight, cWidth, cHeight;
GetBounds( &cr );
pWidth = paperRect.right - paperRect.left;
pHeight = paperRect.bottom - paperRect.top;
cWidth = cr.right - cr.left;
cHeight = cr.bottom - cr.top;
*pagesH = (cWidth / pWidth) + 1;
*pagesV = (cHeight / pHeight) + 1;
}
/*-------------------------------*** PRINTONEPAGE ***---------------------------------*/
/*
draw the requested page (numbered according to the pagination you set up) to the current
port, which will be a printing port when called. By default this manipulates the origin
and draws the content, suitably clipped, to the port. Override for other behaviour.
----------------------------------------------------------------------------------------*/
void ZWindow::PrintOnePage( const short pageNum, const Rect& paperRect )
{
Rect pr;
short hp, vp, dH, dV;
// calculate the printing area
CalcPages( paperRect, &hp, &vp );
pr = paperRect;
dH = paperRect.right * ((pageNum - 1) % hp);
dV = paperRect.bottom * ((pageNum - 1) / hp);
OffsetRect( &pr, dH, dV );
// make sure that the text & pen attributes in the window and printing port match
// in case the DrawContent routine doesn't set them as it goes.
TextFont( macWindow->txFont );
TextSize( macWindow->txSize );
TextFace( macWindow->txFace );
TextMode( srcOr );
PenSize( macWindow->pnSize.h, macWindow->pnSize.v );
PenPat( &macWindow->pnPat );
PenMode( macWindow->pnMode );
// draw the contents.
SetOrigin( pr.left, pr.top );
ClipRect( &pr );
DrawContent();
SetOrigin( 0, 0 );
}
/*------------------------------*** GETCONTENTRECT ***--------------------------------*/
/*
return the content area
----------------------------------------------------------------------------------------*/
void ZWindow::GetContentRect( Rect* contents )
{
*contents = macWindow->portRect;
}
/*-----------------------------------*** CLICK ***------------------------------------*/
/*
the user clicked in the content area of this window. By default this does simple tracking.
----------------------------------------------------------------------------------------*/
void ZWindow::Click( const Point mouse, const short modifiers )
{
#if _INSTALL_STD_MOUSE_TRACKING
if ( ! Floats())
{
ZMouseTracker mt( this );
mt.Track( mouse );
}
#endif
}
/*-----------------------------*** CLICKINSAMEPLACE ***-------------------------------*/
/*
should this click be considered a double-click? You can override this and return TRUE if
the two points are to be considered in the same place for the purposes of determining
a double-click. The default method always returns TRUE. The points passed are in the
window's LOCAL coordinates.
----------------------------------------------------------------------------------------*/
Boolean ZWindow::ClickInSamePlace( const Point click1, const Point click2 )
{
return TRUE;
}
/*-------------------------------*** ADJUSTCURSOR ***---------------------------------*/
/*
The cursor is over this window. Set its shape according to where it is and what modifiers
are down. By default this just sets the cursor to an arrow. <mouse> is in local coords.
----------------------------------------------------------------------------------------*/
void ZWindow::AdjustCursor( const Point mouse, const short modifiers )
{
ResumeCursorAnimation();
SetCursorShape( ARROW_CURSOR );
}
/*-------------------------------*** SETSIZERECT ***----------------------------------*/
/*
set minimum and maximum sizes for this window
----------------------------------------------------------------------------------------*/
void ZWindow::SetSizeRect( const Rect& szRect )
{
sizeRect = szRect;
// make sure max is not less than min:
sizeRect.right = MAX( sizeRect.right, sizeRect.left );
sizeRect.bottom = MAX( sizeRect.bottom, sizeRect.top );
// if the window does not conform to the size constraints, change its size so
// that it does.
Rect pr;
pr = macWindow->portRect;
if (( pr.right - pr.left ) < sizeRect.left ||
( pr.right - pr.left ) > sizeRect.right ||
( pr.bottom - pr.top ) < sizeRect.top ||
( pr.bottom - pr.top ) > sizeRect.bottom )
SetSize( pr.right - pr.left, pr.bottom - pr.top, FALSE );
}
/*-------------------------------*** GETSIZERECT ***----------------------------------*/
/*
get minimum and maximum sizes for this window
----------------------------------------------------------------------------------------*/
void ZWindow::GetSizeRect( Rect* szRect )
{
*szRect = sizeRect;
}
/*----------------------------------*** CLOSE ***-------------------------------------*/
/*
we want to close this window. If it needs to be saved, do that. The user may cancel this.
----------------------------------------------------------------------------------------*/
Boolean ZWindow::Close( const short phase )
{
// the user wants to close the window. This checks the dirty flag and if dirty, asks the
// user if they want to save changes, if so, this calls save.
// first see if we have any subsidiary windows and ask them to close
if (! CloseSubsidiaryWindows( phase ))
return FALSE;
// they were all closed successfully, now check this one
short check = kCloseNoSave;
if ( dirty )
{
// we are about to show the "Save Changes?" alert- so make sure we are frontmost and
// fully updated. This also ensures that a window that is hidden will be correctly
// reshown under this situation, but not otherwise.
Select();
PerformUpdate();
#if _USE_NAV_SAVEREVERT_ALERTS
if ( gMacInfo.hasNavigationServices )
{
// use nav services version of the "save changes" dialog:
NavDialogOptions navOptions;
NavAskSaveChangesResult navResult;
NavAskSaveChangesAction navAction;
FailOSErr( NavGetDefaultDialogOptions( &navOptions ));
gApplication->GetName( navOptions.clientName );
GetName( navOptions.savedFileName );
navAction = (phase == kQuitting)? kNavSaveChangesQuittingApplication : kNavSaveChangesClosingDocument;
FailOSErr( NavAskSaveChanges( &navOptions,
navAction,
&navResult,
gNavEventHandler,
(NavCallBackUserData) this ));
// convert nav result back to MacZoop constants:
switch ( navResult )
{
case kNavAskSaveChangesSave:
check = kConfirmSave;
break;
case kNavAskSaveChangesCancel:
check = kCloseCancel;
break;
case kNavAskSaveChangesDontSave:
check = kCloseNoSave;
break;
}
}
else
{
#endif
Str31 nameStr;
Str31 phaseStr;
GetName( nameStr );
GetIndString( phaseStr, kMiscStrListID, ( phase == kRunning )? 1 : 2);
ParamText( nameStr, phaseStr, NULL, NULL );
SetCursorShape( 0 );
check = NotifyAlert( kConfirmSaveAlertID ); // do you wish to save?
#if _USE_NAV_SAVEREVERT_ALERTS
}
#endif
if ( check == kConfirmSave )
if (! Save( FALSE )) // if so, save the file
check = kCloseCancel;
}
if ( check != kCloseCancel )
{
Hide();
SendMessage( kMsgWindowClosing, NULL ); // tell interested parties we're going away
ForgetThis();
return TRUE; // was closed
}
else
return FALSE; // wasn't closed
}
/*---------------------------*** CLOSESUBSIDIARYWINDOWS ***---------------------------*/
/*
ask any windows supervised by this one to close
----------------------------------------------------------------------------------------*/
Boolean ZWindow::CloseSubsidiaryWindows( const short phase )
{
Boolean allClosed = TRUE;
if ( itsUnderlings )
{
// there are some commanders supervised by this object. If they are
// window objects, call their close method
ZWindow* w;
long i = itsUnderlings->CountItems();
while( i )
{
// use RTTI to make sure the object is some sort of window
w = dynamic_cast<ZWindow*>(itsUnderlings->GetObject( i-- ));
if (w)
{
// yes it is, so ask it to close
if (! w->Close( phase ))
{
// if it didn't close, then abandon the closure sequence
allClosed = FALSE;
break;
}
}
}
}
return allClosed;
}
/*--------------------------------*** SENDBEHIND ***----------------------------------*/
/*
sends this window behind <aWindow> (or behind all if NULL).
----------------------------------------------------------------------------------------*/
void ZWindow::SendBehind( ZWindow* aWindow )
{
gWindowManager->MoveWindowBehind( this, aWindow );
}
/*-----------------------------------*** HIDE ***-------------------------------------*/
/*
make this window invisible
----------------------------------------------------------------------------------------*/
void ZWindow::Hide()
{
Boolean wasVis = IsVisible();
gWindowManager->HideWindow( this );
if ( wasVis )
gWindowManager->ZoomWindowClosed( this );
}
/*-----------------------------------*** SHOW ***-------------------------------------*/
/*
make this window visible
----------------------------------------------------------------------------------------*/
void ZWindow::Show()
{
gWindowManager->ShowWindow( this );
}
/*---------------------------------*** SELECT ***-------------------------------------*/
/*
make this window the active window
----------------------------------------------------------------------------------------*/
void ZWindow::Select()
{
Boolean wasVis = IsVisible();
gWindowManager->SelectWindow( this );
if ( ! wasVis )
PerformUpdate();
}
/*---------------------------------*** PLACEAT ***------------------------------------*/
/*
position the window on the screen at h, v.
----------------------------------------------------------------------------------------*/
void ZWindow::PlaceAt( const short hGlobal, const short vGlobal )
{
MoveWindow( macWindow, hGlobal, vGlobal, FALSE );
}
/*----------------------------------*** PLACE ***-------------------------------------*/
/*
place the window in it's default location. By default this just calls the window manager's
InitiallyPlace() method. Override it for special placement.
----------------------------------------------------------------------------------------*/
void ZWindow::Place()
{
gWindowManager->InitiallyPlace( this );
if ( macFile.vRefNum != kNoFile && ! IsVisible())
RestorePosition();
}
/*------------------------------*** PLACERELATIVE ***---------------------------------*/
/*
place the window relative to another window or the screen
----------------------------------------------------------------------------------------*/
void ZWindow::PlaceRelative( ZWindow* relWindow, WindowPlacing aPlacing )
{
short h, v, hh, vv;
short ww, wh, sw, sh;
GDHandle sDev;
GetContentRegion( gUtilRgn );
ww = (*gUtilRgn)->rgnBBox.right - (*gUtilRgn)->rgnBBox.left;
wh = (*gUtilRgn)->rgnBBox.bottom - (*gUtilRgn)->rgnBBox.top;
if ( relWindow && ( aPlacing == kCentreOnParent ||
aPlacing == kAlertPositionOnParent ||
aPlacing == kStaggerOnParent ))
{
relWindow->GetContentRegion( gUtilRgn );
sw = (*gUtilRgn)->rgnBBox.right - (*gUtilRgn)->rgnBBox.left;
sh = (*gUtilRgn)->rgnBBox.bottom - (*gUtilRgn)->rgnBBox.top;
hh = (*gUtilRgn)->rgnBBox.left;
vv = (*gUtilRgn)->rgnBBox.top;
}
else
{
sDev = GetMainDevice();
sw = (*sDev)->gdRect.right;
sh = (*sDev)->gdRect.bottom;
hh = vv = 0;
}
if ( aPlacing == kStaggerOnParent )
{
h = hh + 10;
v = vv + 20;
}
else
{
// centre on something
h = hh + ( sw / 2 ) - ( ww / 2 );
if ( aPlacing == kAlertPositionOnParent ||
aPlacing == kAlertPositionOnScreen )
v = vv + ( sh / 3 ) - ( wh / 2 );
else
v = vv + ( sh / 2 ) - ( wh / 2 );
}
PlaceAt( h, v );
}
/*-------------------------------*** SAVEPOSITION ***---------------------------------*/
/*
save the window's position in a 'Wpos' resource. If the window has a file, it's saved
there. If not, the global prefs file (if any) is used. If the id passed is 0, the windID
is used as a resource ID.
----------------------------------------------------------------------------------------*/
void ZWindow::SavePosition( short id )
{
#if _WPOS_WINDOW_PLACEMENT
if ( id == 0 )
id = windID;
ZResourceFile* aFile = NULL;
if ( macFile.vRefNum != kNoFile )
FailNIL( aFile = new ZResourceFile( macFile ));
gWindowManager->SaveWindowPosition( this, aFile, id );
if ( aFile )
ForgetObject( aFile );
#endif
}
/*-----------------------------*** RESTOREPOSITION ***--------------------------------*/
/*
restores the window's position from a 'WPos' resource, either in its own file or the
prefs, if any. If id is 0, the windID is used to identify the resource.
----------------------------------------------------------------------------------------*/
void ZWindow::RestorePosition( short id )
{
#if _WPOS_WINDOW_PLACEMENT
if ( id == 0 )
id = windID;
ZResourceFile* aFile = NULL;
if ( macFile.vRefNum != kNoFile )
FailNIL( aFile = new ZResourceFile( macFile ));
gWindowManager->RestoreWindowPosition( this, aFile, id );
if ( aFile )
ForgetObject( aFile );
#endif
}
#pragma mark -
/*------------------------------*** HANDLECOMMAND ***---------------------------------*/
/*
handle commands for a window, like Save, Save As, Close, etc.
----------------------------------------------------------------------------------------*/
void ZWindow::HandleCommand( const long aCmd )
{
EventRecord ev;
switch ( aCmd )
{
case kCmdClose:
gApplication->GetCurrentEvent( &ev );
if ( ev.modifiers & optionKey )
gApplication->CloseAll( Floats());
else
Close( kRunning );
break;
case kCmdSave:
Save( FALSE );
break;
case kCmdSaveAs:
Save( TRUE );
break;
case kCmdRevert:
Revert();
break;
default:
// pass other commands up to this object's boss
ZCommander::HandleCommand( aCmd );
break;
}
}
/*------------------------------*** HANDLECOMMAND ***---------------------------------*/
void ZWindow::HandleCommand( const short menuID, const short itemID )
{
ZCommander::HandleCommand( menuID, itemID );
}
/*--------------------------------*** UPDATEMENUS ***---------------------------------*/
/*
update the menus for a window, like Save, Save As, Close, etc.
----------------------------------------------------------------------------------------*/
void ZWindow::UpdateMenus()
{
// this enables the Close, Save and SaveAs commands.
EventRecord ev;
gApplication->GetCurrentEvent( &ev );
// show "Close All" if the option key is down, else "Close"
if (ev.modifiers & optionKey )
gMenuBar->SetCommandText( kCmdClose, kMiscStrListID, 8 );
else
gMenuBar->SetCommandText( kCmdClose, kMiscStrListID, 9 );
gMenuBar->EnableCommand( kCmdClose );
if ( dirty )
{
gMenuBar->EnableCommand( kCmdSave );
if ( isNamed )
gMenuBar->EnableCommand( kCmdRevert );
}
gMenuBar->EnableCommand( kCmdSaveAs );
// call the boss to enable her menus
ZCommander::UpdateMenus();
}
/*-----------------------------------*** SETTASK ***----------------------------------*/
/*
submit an undoable task for this window. This updates the global undo task and marks the
document as dirty.
----------------------------------------------------------------------------------------*/
void ZWindow::SetTask( ZUndoTask* aTask )
{
// if we're not dirty when the task arrives, then this must be the first task. Let it
// know so that if the task is undone, it can clear the dirty flag so that undoing the
// first task doesn't result in the "save changes?" alert.
if ( ! dirty && aTask )
aTask->SetIsFirstTask();
dirty = TRUE; // document has been changed by the task
gApplication->SetTask( aTask );
}
/*---------------------------------*** SETSIZE ***------------------------------------*/
/*
set this window to the width and height passed
----------------------------------------------------------------------------------------*/
void ZWindow::SetSize( const short width, const short height, const Boolean reDraw )
{
// sets the window's size to the width and height passed, constrained to sizeRect.
short w, h;
w = MIN( sizeRect.right, MAX( width, sizeRect.left ));
h = MIN( sizeRect.bottom, MAX( height, sizeRect.top ));
if (( w != macWindow->portRect.right - macWindow->portRect.left ) ||
( h != macWindow->portRect.bottom - macWindow->portRect.top ))
{
Focus();
if ( reDraw )
EraseRect( &macWindow->portRect );
SizeWindow( macWindow, w, h, reDraw );
WindowResized();
if ( reDraw )
Draw();
}
}
/*-----------------------------------*** ZOOM ***-------------------------------------*/
/*
Zoom this window between the standard and user states. This implements intelligent zoom-
ing as recommended by the Apple HIG people. It may appear complex and indeed it probably
is, however, the results are worth it since the zooming of windows almost always does
exactly the most correct thing for your data- automatically! It also deals with any number
of multiple monitors. You can always override this if you want something different.
Based largely on code by Dean Yu, Apple Computer Inc.
----------------------------------------------------------------------------------------*/
#define kNudgeSlop 2
#define kIconAllowance 64
#define kTabFolderAllowance 20
void ZWindow::Zoom( const short partCode )
{
RgnHandle contRgn, strucRgn, scratchRgn;
Rect portRect, crBBox, srBBox, zwBounds, wpOnScreen;
short wfTop, wfLeft, wfBottom, wfRight;
long largestArea = 0;
GDHandle aScreen, targetMonitor;
Focus();
EraseRect( &macWindow->portRect );
// calculate the most intelligent zoom state for the window and set the
// standard state to it. The most intelligent state is on the monitor with
// the largest area of the window, and that involves the minimum amount of
// window movement.
if ( partCode == inZoomOut )
{
GetContentRegion( contRgn = NewRgn());
GetStructureRegion( strucRgn = NewRgn());
portRect = macWindow->portRect;
crBBox = (*contRgn)->rgnBBox;
srBBox = (*strucRgn)->rgnBBox;
// calculate the window frame size
wfTop = crBBox.top - srBBox.top;
wfLeft = crBBox.left - srBBox.left;
wfBottom = srBBox.bottom - crBBox.bottom;
wfRight = srBBox.right - crBBox.right;
// the ideal size for this window is the max sizeRect. For scrollable windows,
// this is set according to the bounds, which will give us the expected results
// we now need to find the monitor that has the largest portion of the window
// intersecting it.
scratchRgn = NewRgn();
SectRgn( GetGrayRgn(), contRgn, scratchRgn );
if (EmptyRgn( scratchRgn ))
zwBounds = srBBox;
else
zwBounds = crBBox;
DisposeRgn( scratchRgn );
// re-use srBBox for the new standard state rect, initially set to the ideal
// window size if we had an infinitely large monitor.
GetIdealWindowZoomSize( &srBBox );
// walk the device list to determine which monitor the window should be
// zoomed to:
aScreen = GetDeviceList();
while (aScreen)
{
long wpArea;
// find the intersection of the window and the screen
SectRect( &zwBounds, &(*aScreen)->gdRect, &wpOnScreen );
// find the area of this portion
OffsetRect( &wpOnScreen, -wpOnScreen.left, -wpOnScreen.top );
wpArea = (long) wpOnScreen.right * (long) wpOnScreen.bottom;
// if this is larger than the area found so far, keep track of
// the monitor that contains it
if (wpArea > largestArea)
{
largestArea = wpArea;
targetMonitor = aScreen;
}
// look at the next monitor in the list
aScreen = GetNextDevice( aScreen );
}
// ok, we have found the monitor we wish to zoom to. Store the monitor's
// global rect in the wpOnScreen variable
wpOnScreen = (*targetMonitor)->gdRect;
// allow for the menubar and desktop icons if it's the main one
if (targetMonitor == GetMainDevice())
{
wpOnScreen.top += GetMBarHeight();
wpOnScreen.right -= kIconAllowance;
// if we have tabbed folders (8.x), allow space for it
if ( gMacInfo.systemVersion >= 0x0800 )
wpOnScreen.bottom -= kTabFolderAllowance;
}
// OK, we can now calculate the standard rect we wish to zoom to (finally).
// We calculate this using srBBox, since that now holds the new std state
OffsetRect( &srBBox, crBBox.left, crBBox.top );
srBBox.top -= wfTop;
srBBox.left -= wfLeft;
srBBox.right += wfRight;
srBBox.bottom += wfBottom;
// this is the ideal size for the window content. See if it needs nudging onto the
// monitor
SectRect( &srBBox, &wpOnScreen, &zwBounds );
if (! EqualRect( &srBBox, &zwBounds ))
{
// needs to be nudged onto the monitor.
short oH, oV;
oH = CalculateOffsetAmount( srBBox.left, srBBox.right,
zwBounds.left, zwBounds.right,
wpOnScreen.left, wpOnScreen.right );
oV = CalculateOffsetAmount( srBBox.top, srBBox.bottom,
zwBounds.top, zwBounds.bottom,
wpOnScreen.top, wpOnScreen.bottom );
OffsetRect( &srBBox, oH, oV );
}
// If this still falls off the screen in any direction, it means that it is too large
// for the montior, so it will need to be shrunk down to fit.
SectRect( &srBBox, &wpOnScreen, &zwBounds);
if (! EqualRect( &srBBox, &zwBounds ))
{
// nope- still doesn't fit. So shrink it down.
if ((srBBox.right - srBBox.left) > (wpOnScreen.right - wpOnScreen.left))
{
srBBox.left = wpOnScreen.left + kNudgeSlop;
srBBox.right = wpOnScreen.right - kNudgeSlop;
}
if ((srBBox.bottom - srBBox.top) > (wpOnScreen.bottom - wpOnScreen.top))
{
srBBox.top = wpOnScreen.top + kNudgeSlop;
srBBox.bottom = wpOnScreen.bottom - kNudgeSlop;
}
}
// adjust for the thickness of the frame and set the standard rect
srBBox.top += wfTop;
srBBox.left += wfLeft;
srBBox.right -= wfRight;
srBBox.bottom -= wfBottom;
SetStdZoomRect( srBBox );
DisposeRgn( contRgn );
DisposeRgn( strucRgn );
}
// and.... Zoom!
ZoomWindow( macWindow, partCode, FALSE );
// notify resize of window
WindowResized();
}
/*--------------------------*** GETIDEALWINDOWZOOMSIZE ***----------------------------*/
/*
return the optimum ideal size for a zoomed window. By default this is equal to the
max size rectangle, but you can override this if you know better.
----------------------------------------------------------------------------------------*/
void ZWindow::GetIdealWindowZoomSize( Rect* idealSize )
{
idealSize->top = idealSize->left = 0;
idealSize->right = sizeRect.right;
idealSize->bottom = sizeRect.bottom;
}
/*------------------------------*** SETSTDZOOMRECT ***--------------------------------*/
/*
set the std rect for zoomable windows. The window zooms to this rect when the zoom box
is clicked. If the window is not zoomable, this does nothing. The rect is in global coords.
----------------------------------------------------------------------------------------*/
void ZWindow::SetStdZoomRect( const Rect& aRect )
{
short v = GetWVariant( macWindow );
if (v == zoomDocProc ||
v == zoomNoGrow)
SetWindowStandardState( macWindow, &aRect ); // using copland macro
}
/*------------------------------*** SETUSERZOOMRECT ***-------------------------------*/
/*
set the user rect for zoomable windows. The window zooms to this rect when the zoom box
is clicked and zoomed out. The rect is in global coords.
----------------------------------------------------------------------------------------*/
void ZWindow::SetUserZoomRect( const Rect& aRect )
{
short v = GetWVariant( macWindow );
if (v == zoomDocProc ||
v == zoomNoGrow)
SetWindowUserState( macWindow, &aRect ); // using copland macro
}
#pragma mark -
/*-----------------------------------*** SAVE ***-------------------------------------*/
/*
save the contents of the window to a file. This may display the standard file dialog.
----------------------------------------------------------------------------------------*/
Boolean ZWindow::Save( const Boolean forceSaveAs )
{
// handles the save and save as commands.
Boolean requiresNavPostProcess = FALSE;
#if _USE_NAVIGATION_SERVICES
NavReplyRecord navReply;
OSErr theErr;
#endif
if (! isNamed || forceSaveAs)
{
// do Save As
#if _USE_NAVIGATION_SERVICES
if ( gMacInfo.hasNavigationServices )
{
PickFile( &navReply );
if ( navReply.validRecord )
{
AEDesc specDesc;
FInfo fi;
FailOSErr( AEGetNthDesc( &navReply.selection, 1, typeFSS, NULL, &specDesc ));
BlockMoveData( *specDesc.dataHandle, &macFile, sizeof( FSSpec ));
// to get the type we need to do a FSpGetFInfo, which may return fnfErr
theErr = FSpGetFInfo( &macFile, &fi );
if ( theErr == noErr )
macFType = fi.fdType;
requiresNavPostProcess = TRUE;
}
else
{
NavDisposeReply( &navReply );
return FALSE;
}
}
else
{
#endif
StandardFileReply macReply;
PickFile( &macReply );
if ( macReply.sfGood )
macFile = macReply.sfFile;
else
return FALSE; // user cancelled the save
#if _USE_NAVIGATION_SERVICES
}
#endif
}
// now do a save with the info.
SetWatchCursor();
SaveFile();
#if _USE_NAVIGATION_SERVICES
if ( gMacInfo.hasNavigationServices && requiresNavPostProcess )
{
NavCompleteSave( &navReply, kNavTranslateInPlace );
NavDisposeReply( &navReply );
}
#endif
return TRUE;
}
/*---------------------------------*** PICKFILE ***-----------------------------------*/
/*
display standard file dialog for picking a file for saving. Override if you want custom
dialog, etc.
----------------------------------------------------------------------------------------*/
void ZWindow::PickFile( StandardFileReply* macReply )
{
Str31 prompt;
Str255 name;
GetIndString( prompt, kMiscStrListID, 3 );
GetName( name );
// make sure that filename is no longer than 31 chars
name[0] = MIN( name[0], 31 );
// here, we use the "classic" standard file dialog:
gWindowManager->DeactivateForDialog( sfPutDialogID );
StandardPutFile( prompt, name, macReply );
gWindowManager->Activate();
}
#if _USE_NAVIGATION_SERVICES
void ZWindow::PickFile( NavReplyRecord* navReply )
{
OSErr theErr;
NavDialogOptions navOptions;
FailOSErr( NavGetDefaultDialogOptions( &navOptions ));
gApplication->GetName( navOptions.clientName );
GetName( navOptions.savedFileName );
gWindowManager->DeactivateForDialog( -1, FALSE );
theErr = NavPutFile( NULL,
navReply,
&navOptions,
gNavEventHandler,
macFType,
gAppSignature,
(NavCallBackUserData) this );
gWindowManager->Activate();
}
#endif
/*---------------------------------*** SAVEFILE ***-----------------------------------*/
/*
write out the contents of the window to a file. This may rename the window. Note that you
need to override this to actually write to a file (using a ZFile, perhaps), then call the
inherited method to set the name and the state flags.
----------------------------------------------------------------------------------------*/
void ZWindow::SaveFile()
{
// actually save the window's contents to the file.
if ( macFile.vRefNum != kNoFile )
{
SetTitle( macFile.name );
// no longer dirty
isNamed = TRUE;
dirty = FALSE;
// save the window's position to the file
SavePosition();
}
}
/*---------------------------------*** GETNAME ***------------------------------------*/
/*
return the name of the window
----------------------------------------------------------------------------------------*/
void ZWindow::GetName( Str255 name )
{
GetWTitle( macWindow, name );
}
/*----------------------------------*** REVERT ***------------------------------------*/
/*
revert the contents of the window to the original file
----------------------------------------------------------------------------------------*/
void ZWindow::Revert()
{
// revert the contents. By default, this just calls open.
#if _USE_NAV_SAVEREVERT_ALERTS
if ( gMacInfo.hasNavigationServices )
{
NavDialogOptions navOptions;
NavAskDiscardChangesResult navResult;
GetName( navOptions.savedFileName );
FailOSErr( NavAskDiscardChanges( &navOptions,
&navResult,
gNavEventHandler,
(NavCallBackUserData) this ));
if ( navResult == kNavAskDiscardChanges )
{
SetWatchCursor();
OpenFile( macFType );
}
}
else
{
#endif
Str31 title;
GetName( title );
ParamText( title, NULL, NULL, NULL );
if ( NotifyAlert( kRevertConfirmAlertID ) == ok )
{
SetWatchCursor();
OpenFile( macFType );
}
#if _USE_NAV_SAVEREVERT_ALERTS
}
#endif
}
/*---------------------------------*** SETFILE ***------------------------------------*/
/*
set the filespec for this window to the file passed.
----------------------------------------------------------------------------------------*/
void ZWindow::SetFile( const FSSpec& aFile )
{
macFile = aFile;
}
/*---------------------------------*** OPENFILE ***-----------------------------------*/
/*
open the file into the window's contents. This will set the name of the window to equal
the filename
----------------------------------------------------------------------------------------*/
void ZWindow::OpenFile( const OSType aFileType, Boolean isStationery )
{
// this opens the current file into this window. This function should read the
// contents of the file, replacing the current contents.
// You should check that the vRefNum of the file spec is not kNoFile.
// Call the inherited method to maintain the proper window state variables.
if ( macFile.vRefNum != kNoFile )
{
macFType = aFileType;
if ( ! isStationery )
{
SetTitle(macFile.name);
isNamed = TRUE;
}
stationeryFile = isStationery;
if ( IsVisible())
PostRefresh();
// delete any existing undo task for this window after opening a file,
// since it probably contains stale data.
ZUndoTask* task = gApplication->GetUndoTask();
if ( task && ( task->GetUndoTarget() == this ))
gApplication->SetTask( NULL );
dirty = FALSE;
}
}
/*---------------------------------*** SETTITLE ***-----------------------------------*/
/*
set the title of the window
----------------------------------------------------------------------------------------*/
void ZWindow::SetTitle( Str255 aTitle )
{
SetWTitle( macWindow, aTitle );
}
/*---------------------------------*** ISVISIBLE ***----------------------------------*/
/*
Is the window visible on screen?
----------------------------------------------------------------------------------------*/
Boolean ZWindow::IsVisible()
{
if ( macWindow )
return ((WindowPeek) macWindow)->visible;
else
return FALSE;
}
/*----------------------------------*** ISACTIVE ***----------------------------------*/
/*
Is the window currently activated?
----------------------------------------------------------------------------------------*/
Boolean ZWindow::IsActive()
{
if ( macWindow )
return ((WindowPeek) macWindow)->hilited;
else
return FALSE;
}
/*-----------------------------*** GETTITLEBARHEIGHT ***------------------------------*/
/*
return the actual height of the window's title bar. This measures it from the regions
and is thus totally WDEF safe.
----------------------------------------------------------------------------------------*/
short ZWindow::GetTitleBarHeight()
{
RgnHandle sRgn, cRgn;
short tHeight;
FailNIL( sRgn = NewRgn());
FailNIL( cRgn = NewRgn());
GetStructureRegion( sRgn );
GetContentRegion( cRgn );
tHeight = (*cRgn)->rgnBBox.top - (*sRgn)->rgnBBox.top;
DisposeRgn( sRgn );
DisposeRgn( cRgn );
return tHeight;
}
/*----------------------------*** GETSTRUCTUREREGION ***------------------------------*/
/*
copies the window's structure region into the passed region
----------------------------------------------------------------------------------------*/
void ZWindow::GetStructureRegion( RgnHandle aRgn )
{
FailNILParam( aRgn );
WindowPeek w = ( WindowPeek ) macWindow;
Boolean wasFudged = FALSE;
short h, v;
Rect saveUserRect;
// if the window isn't visible, the structure region isn't valid, so we have to
// make it visible offscreen in order to get the region
if ( ! w->visible )
{
GetWindowUserState( macWindow, &saveUserRect );
GetGlobalPosition( &h, &v );
MoveWindow( macWindow, h + 10000, v + 100, FALSE );
ShowHide( macWindow, TRUE );
wasFudged = TRUE;
}
CopyRgn( w->strucRgn, aRgn );
if ( wasFudged )
{
ShowHide( macWindow, FALSE );
MoveWindow( macWindow, h, v, FALSE );
OffsetRgn( aRgn, -10000, -100 );
SetWindowUserState( macWindow, &saveUserRect );
}
}
/*-----------------------------*** GETCONTENTREGION ***-------------------------------*/
/*
copies the window's content region into the passed region
----------------------------------------------------------------------------------------*/
void ZWindow::GetContentRegion( RgnHandle aRgn )
{
FailNILParam( aRgn );
WindowPeek w = ( WindowPeek ) macWindow;
Boolean wasFudged = FALSE;
short h, v;
Rect saveUserRect;
// if the window isn't visible, the content region isn't valid, so we have to
// make it visible offscreen in order to get the region
if ( ! w->visible )
{
GetWindowUserState( macWindow, &saveUserRect );
GetGlobalPosition( &h, &v );
MoveWindow( macWindow, h + 10000, v + 100, FALSE );
ShowHide( macWindow, TRUE );
wasFudged = TRUE;
}
CopyRgn( w->contRgn, aRgn );
if ( wasFudged )
{
ShowHide( macWindow, FALSE );
MoveWindow( macWindow, h, v, FALSE );
OffsetRgn( aRgn, -10000, -100 );
SetWindowUserState( macWindow, &saveUserRect );
}
}
/*-------------------------*** GETSTRUCTUREFRAMEBORDER ***----------------------------*/
/*
returns in <aRect> the thicknesses of the window borders. <top> is the title bar height,
<left> and <right> are the thicknesses of the edges, and <bottom> is the thickness of the
bottom.
----------------------------------------------------------------------------------------*/
void ZWindow::GetStructureFrameBorder( Rect* aRect )
{
RgnHandle sRgn, cRgn;
Rect s, c;
FailNILParam( aRect );
FailNIL( sRgn = NewRgn());
FailNIL( cRgn = NewRgn());
GetStructureRegion( sRgn );
GetContentRegion( cRgn );
s = (*sRgn)->rgnBBox;
c = (*cRgn)->rgnBBox;
aRect->top = c.top - s.top;
aRect->left = c.left - s.left;
aRect->right = s.right - c.right;
aRect->bottom = s.bottom - c.bottom;
DisposeRgn( sRgn );
DisposeRgn( cRgn );
}
/*-----------------------------*** GETGLOBALPOSITION ***------------------------------*/
/*
returns the global position of the window on screen
----------------------------------------------------------------------------------------*/
void ZWindow::GetGlobalPosition( short* hGlobal, short* vGlobal )
{
GrafPtr savePort;
Point gloc;
GetPort( &savePort );
SetPort( macWindow );
gloc = topLeft( macWindow->portRect );
LocalToGlobal( &gloc );
SetPort( savePort );
*hGlobal = gloc.h;
*vGlobal = gloc.v;
}
/*-------------------------------*** WRITETOSTREAM ***--------------------------------*/
/*
write the window state to the stream. You can save an entire window to the stream and
recreate it exactly later on without needing any resources, etc.
----------------------------------------------------------------------------------------*/
void ZWindow::WriteToStream( ZStream* aStream )
{
#if _MACZOOP_STREAMS
ZCommander::WriteToStream( aStream );
// save window state info to the stream. This includes its position, title, visible
// and floating flags, ID, etc, etc.
short h, v;
Str255 title;
// write essential data stored in <macWindow> first...
aStream->WriteRect( &macWindow->portRect );
aStream->WriteChar( IsVisible());
aStream->WriteShort( GetWVariant( macWindow ));
GetName( title );
aStream->WriteString( title );
aStream->WriteChar(((WindowPeek) macWindow )->goAwayFlag );
// write grafport state
aStream->WriteGrafPort( macWindow );
// write global window position...
GetGlobalPosition( &h, &v );
aStream->WriteShort( h );
aStream->WriteShort( v );
// write various data members...
aStream->WriteRect( &sizeRect );
aStream->WriteShort( windID );
aStream->WriteChar( isNamed );
aStream->WriteChar( stationeryFile );
aStream->WriteData((Ptr) &macFile, sizeof( FSSpec ));
aStream->WriteLong((long) macFType );
aStream->WriteChar( printable );
aStream->WriteChar( floating );
aStream->WriteChar( disableAutoClose );
aStream->WriteRect( &zoomSource );
#endif
}
/*------------------------------*** READFROMSTREAM ***--------------------------------*/
/*
recover the window state from the stream. Normally this is called to actually create a
window from the stream (i.e. InitZWindow is not called). Thus this uses the stream data
to make the window and should not be used to change an existing one.
----------------------------------------------------------------------------------------*/
void ZWindow::ReadFromStream( ZStream* aStream )
{
#if _MACZOOP_STREAMS
ZCommander::ReadFromStream( aStream );
Rect pr;
Boolean vis, goAway;
short varCode, h, v;
long dLen;
Str255 title;
aStream->ReadRect( &pr );
aStream->ReadChar((char*) &vis );
aStream->ReadShort( &varCode );
aStream->ReadString( title );
aStream->ReadChar((char*) &goAway );
MakeMacWindow( &pr, title, vis, varCode, goAway );
// read grafport state
aStream->ReadGrafPort( macWindow );
// read the rest of our data...
aStream->ReadShort( &h );
aStream->ReadShort( &v );
MoveWindow( macWindow, h, v, FALSE );
aStream->ReadRect( &sizeRect );
aStream->ReadShort( &windID );
aStream->ReadChar((char*) &isNamed );
aStream->ReadChar((char*) &stationeryFile );
dLen = sizeof( FSSpec );
aStream->ReadData((Ptr) &macFile, &dLen );
aStream->ReadLong((long*) &macFType );
aStream->ReadChar((char*) &printable );
aStream->ReadChar((char*) &floating );
aStream->ReadChar((char*) &disableAutoClose );
aStream->ReadRect( &zoomSource );
// register with window manager...
gWindowManager->AddWindow( this );
#endif
}
#pragma mark -
/*------------------------------------*** DRAG ***------------------------------------*/
/*
initiate a drag from this window
----------------------------------------------------------------------------------------*/
Boolean ZWindow::Drag( const Point startPt )
{
// this can be called to instigate a drag from this window. When your mouse dragging method
// wants to perform a drag, it calls this. This then builds the drag region and drag data
// by calling some additional methods. You can override those methods to implement the drag
// data, etc you require. <startPt> is in local coordinates, as passed from Click, e.g.
DragReference theDrag;
RgnHandle dragRgn = NULL;
unsigned short diCount;
EventRecord theEvent;
Boolean result = TRUE;
if ( MacHasDM())
{
// make absolutely sure we are the current port, etc.
Focus();
FailOSErr( NewDrag( &theDrag ));
// add data to the drag
try
{
MakeDragData( theDrag );
// if no data was added to the drag (the default case, in fact) do not
// bother to do anything else
FailOSErr( CountDragItems( theDrag, &diCount ));
if ( diCount > 0 )
{
// make a drag region
FailNIL( dragRgn = MakeDragRgn());
// make a dummy event record
theEvent.what = mouseDown;
theEvent.where = startPt;
LocalToGlobal(&theEvent.where);
theEvent.when = TickCount();
theEvent.message = 0;
theEvent.modifiers = 0;
// do that drag manager thang!
FailOSErr( TrackDrag( theDrag, &theEvent, dragRgn ));
}
}
catch ( OSErr err )
{
// do not propagate exceptions
result = FALSE;
}
DisposeDrag( theDrag );
if ( dragRgn )
DisposeRgn( dragRgn );
}
return result;
}
/*--------------------------------*** MAKEDRAGRGN ***---------------------------------*/
/*
create the drag outline. This uses the content rect by default
----------------------------------------------------------------------------------------*/
RgnHandle ZWindow::MakeDragRgn()
{
// override to build the drag region you require. By default, the drag region is the content
// region of the window.
Rect content;
RgnHandle dragRgn, temp;
Point origin = {0,0};
FailNIL( dragRgn = NewRgn());
FailNIL( temp = NewRgn());
GetContentRect( &content );
RectRgn( dragRgn, &content );
CopyRgn( dragRgn, temp );
InsetRgn( temp, 1, 1 );
DiffRgn( dragRgn, temp, dragRgn );
DisposeRgn( temp );
// convert into global corrdinates
LocalToGlobal( &origin );
OffsetRgn( dragRgn, origin.h, origin.v );
return dragRgn;
}
/*---------------------------------*** DRAGHILITE ***---------------------------------*/
/*
hilite the window in response to a drag over it
----------------------------------------------------------------------------------------*/
void ZWindow::DragHilite( const Boolean state, const DragReference theDrag)
{
// hilites the window. This uses the drag manager's default hiliting.
RgnHandle dragHiliteRgn;
Rect content;
Focus();
if ( state )
{
// make the hilite region
FailNIL( dragHiliteRgn = NewRgn());
GetContentRect( &content );
RectRgn( dragHiliteRgn, &content );
(void) ShowDragHilite( theDrag, dragHiliteRgn, TRUE );
DisposeRgn( dragHiliteRgn );
}
else
(void) HideDragHilite( theDrag );
}
/*----------------------------*** INSTALLDRAGHANDLERS ***-----------------------------*/
/*
install the procs that call this during a drag
----------------------------------------------------------------------------------------*/
void ZWindow::InstallDragHandlers()
{
// set up the drag handler proc to call this object when a drag occurs over this window. The
// window's refCon field contains the object reference, so the handler can find this object.
FailOSErr( InstallTrackingHandler( gDragTrackProc, macWindow, 0L ));
FailOSErr( InstallReceiveHandler ( gDragReceiveProc, macWindow, 0L ));
}
/*----------------------------*** REMOVEDRAGHANDLERS ***------------------------------*/
/*
get rid of the handlers. This is called by the destructor
----------------------------------------------------------------------------------------*/
void ZWindow::RemoveDragHandlers()
{
// removes the drag handlers when the window is deleted
OSErr theErr;
theErr = RemoveTrackingHandler( gDragTrackProc, macWindow );
theErr = RemoveReceiveHandler ( gDragReceiveProc, macWindow );
}
/*--------------------------------*** DROPHANDLER ***---------------------------------*/
/*
unpacks the data from an accepted drag and passes it to the Drop method.
----------------------------------------------------------------------------------------*/
void ZWindow::DropHandler( const DragReference theDrag )
{
// method to dispatch drops from the drag manager. This unpacks the drag data, and for
// each flavour that the AcceptsFlavour method returns TRUE for, will call Drop with the
// data of the item.
unsigned short dragItemCount, i;
unsigned short dragFlavourCount, f;
ItemReference iRef;
FlavorType theFlavour;
Boolean atLeastOneAccepted = FALSE;
Size dataSize;
Ptr theData = NULL;
DragHilite( FALSE, theDrag );
FailOSErr( CountDragItems( theDrag, &dragItemCount ));
SetBeachBallCursor();
for ( i = 1; i <= dragItemCount; i++ )
{
// get the item
FailOSErr( GetDragItemReferenceNumber( theDrag, i, &iRef ));
// count the flavours in the item
FailOSErr( CountDragItemFlavors( theDrag, iRef, &dragFlavourCount ));
// for each flavour, if we can accept the flavour, unpack it and pass the data
// to the drop method.
for ( f = 1; f <= dragFlavourCount; f++ )
{
FailOSErr( GetFlavorType( theDrag, iRef, f, &theFlavour ));
if ( AcceptsFlavour( theFlavour ))
{
// get the data for this object
FailOSErr( GetFlavorDataSize( theDrag, iRef, theFlavour, &dataSize ));
// create a buffer big enough to hold the object
FailNIL( theData = NewPtr( dataSize ));
// get the data into the buffer
try
{
FailOSErr( GetFlavorData( theDrag, iRef, theFlavour, theData, &dataSize, 0L ));
// call the method to handle the drop
Drop( theFlavour, theData, dataSize, theDrag );
}
catch( OSErr err )
{
if ( theData )
DisposePtr( theData );
theData = NULL;
throw err;
}
if ( theData )
DisposePtr( theData );
theData = NULL;
atLeastOneAccepted = TRUE;
}
}
}
// if nothing in this drag was accepted (unlikely???), throw an error
// so that the drag manager gives the right feedback
if (! atLeastOneAccepted)
FailOSErr( dragNotAcceptedErr );
}
/*-------------------------------*** TRACKTHEDRAG ***---------------------------------*/
/*
dispatches tracking messages to the appropriate method
----------------------------------------------------------------------------------------*/
void ZWindow::DragDispatch( const DragTrackingMessage theMessage, const DragReference theDrag )
{
// this method handles the tracking of a drag within this window. It calls various other
// methods to implement its behaviour- normally you would override those where necessary
// rather than this, which is quite low-level.
switch ( theMessage )
{
case kDragTrackingEnterHandler:
EnteredHandler( theDrag );
break;
case kDragTrackingEnterWindow:
EnteredWindow( theDrag );
break;
case kDragTrackingInWindow:
InWindow( theDrag );
break;
case kDragTrackingLeaveWindow:
LeftWindow( theDrag );
break;
case kDragTrackingLeaveHandler:
LeftHandler( theDrag );
break;
}
}
/*-------------------------------*** ENTEREDWINDOW ***--------------------------------*/
/*
the window was made the target of the drag. This hilites it if any flavour in the drag is
acceptable.
----------------------------------------------------------------------------------------*/
void ZWindow::EnteredWindow( const DragReference theDrag)
{
// the drag has enetered this window. If the window accepts any of the flavours in the drag,
// hilite the window.
unsigned short dragItemCount;
unsigned short dragFlavourCount;
ItemReference iRef;
FlavorType theFlavour;
FailOSErr(CountDragItems(theDrag, &dragItemCount));
if (dragItemCount)
{
do
{
// for each drag item, count the flavours
FailOSErr(GetDragItemReferenceNumber(theDrag, dragItemCount, &iRef));
FailOSErr(CountDragItemFlavors(theDrag, iRef, &dragFlavourCount));
// for each flavour, see if we can accept it. As soon as we get one that we can
// handle, we hilite the window and exit.
do
{
FailOSErr(GetFlavorType(theDrag, iRef, dragFlavourCount, &theFlavour));
if (AcceptsFlavour(theFlavour))
{
DragHilite(TRUE, theDrag);
return;
}
}
while(--dragFlavourCount);
}
while(--dragItemCount);
}
}
/*---------------------------------*** LEFTWINDOW ***---------------------------------*/
/*
the drag is no longer in this window. This unhilites the drag here.
----------------------------------------------------------------------------------------*/
void ZWindow::LeftWindow( const DragReference theDrag)
{
// Ladies and Gentlemen, the drag has left the window. Please leave in an orderly fashion.
DragHilite( FALSE, theDrag );
}
/*-------------------------------*** SHOWBALLOONHELP ***------------------------------*/
/*
display a help ballon for the passed local rectangle
----------------------------------------------------------------------------------------*/
void ZWindow::ShowBalloonHelp( Rect* localRect, Point tip, HMMessageRecord* hm )
{
OSErr hmErr;
Rect gr = *localRect;
Focus();
LocalToGlobal( &topLeft( gr ));
LocalToGlobal( &botRight( gr ));
LocalToGlobal( &tip );
hmErr = HMShowBalloon( hm, tip, &gr, NULL, 0, 0, kHMRegularWindow );
}
#pragma mark -
/*---------------------------------******************----------------------------------*/
static short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
{
short offsetAmount;
// First check to see if the window fits on the screen in this dimension.
if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
offsetAmount = 0;
else
{
// Find out how much of the window lies off this screen by subtracting the amount of the window
// that is on the screen from the size of the entire window in this dimension. If the window
// is completely offscreen, the offset amount is going to be the distance from the ideal
// starting point to the first edge of the screen.
if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0)
{
// See if the window is lying to the left or above the screen
if (idealEndPoint < screenEdge1)
offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
else
// Otherwise, it’s below or to the right of the screen
offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
}
else
{
// Window is already partially or completely on the screen
offsetAmount = (idealEndPoint - idealStartPoint) -
(idealOnScreenEndPoint - idealOnScreenStartPoint);
// If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
if (offsetAmount != 0)
offsetAmount += kNudgeSlop;
// Check to see which side of the screen the window was falling off of, so that it can be
// nudged in the opposite direction.
if (idealEndPoint > screenEdge2)
offsetAmount = -offsetAmount;
}
}
return offsetAmount;
}
/*--------------------------------*********************---------------------------------*/
/*
static functions follow. Do not modify them directly- all features can be acessed by
overriding the appropriate methods.
----------------------------------------------------------------------------------------*/
static pascal OSErr ZWTrackingHandler(DragTrackingMessage theMsg, WindowPtr theWindow, void* refCon,
DragReference theDrag)
{
ZWindow* zdWindow = NULL;
OSErr theErr = noErr;
// get the object
if ( theWindow )
zdWindow = GetZWindow( theWindow );
try
{
FailNIL( zdWindow );
zdWindow->DragDispatch( theMsg, theDrag );
}
catch( OSErr err )
{
theErr = err;
}
return theErr;
}
/*--------------------------------*********************---------------------------------*/
static pascal OSErr ZWDropHandler( WindowPtr theWindow, void* refCon, DragReference theDrag )
{
ZWindow* zdWindow = NULL;
OSErr theErr = noErr;
// get the object
if ( theWindow )
zdWindow = GetZWindow( theWindow );
try
{
FailNIL( zdWindow );
zdWindow->DropHandler( theDrag );
}
catch( OSErr err )
{
theErr = err;
// if there was an error that aborted the drop, we would like to report it to the
// user. However, this is a bad time to do it, so instead we set <gDragErr> which
// ZApplicaiton will pick up & report next time it loops, using the Notification
// Manager if in background.
if ( err != dragNotAcceptedErr )
gDragErr = err;
}
return theErr;
}
/*--------------------------------*********************---------------------------------*/